Objective-C内存管理(一)

1.1 什么是自动引用计数####

顾名思义,自动引用计数(ARC,Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术,以下是摘自苹果的官方说明。
在Objective-C中采用Automatic Reference Counting(ARC)机制,让编译器来进行内存管理。在新一代Apple LLVM编译中设置ARC为有效状态,就无需再次键入Retain或者Release代码,这在降低程序崩溃,内存泄漏等风险的同时,很大一定程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。
这些优点无疑具吸引力,但关于ARC技术,最重要的还是下面这一点
“在LLVM编译器中设置ARC为有效状态,无需再次键入retain或者release代码”
换言之,若满足以下条件,就无需手动输入retain或者release代码了。

1 使用Xcode4.2或者以上版本
2 使用LLVM编译器3.0或者以上版本
3 编译器选项中ARC设置为有效

在以上条件下编译源代码时 编译器将自动进行内存管理,这正是每个程序员梦寐以求的。在正式讲解精彩的ARC技术之前,我们先来了解一下,在此之前,程序员在代码中是如何手工进行内存管理的。

1.2 内存管理/引用计数

Objective-C中的内存管理,也就是引用计数管。可以用来开关房间的灯为例来说明引用计数的机制。
假设办公室的照明设备只有一个。上班进入办公室的人需要照明,所以要把灯打开。而对于下班离开办公室的人来说,已经不再需要照明了,所以要把灯关掉。若是很多人上下班,每个人开灯或是关灯,那么办公室的情况有将是一个什么样子呢?最早下班的人如果关了灯,办公室的剩下的人将会处于一片漆黑之中。
解决这一问题的办法是办公室在至少还有一个人的情况下,要保持一个开灯的状态,而在完全没有人的情况下,保持关灯状态。

最早进入办公室的人开灯
之后需要进入办公室的人需要照明
下班离开办公室的人不需要照明
最后离开办公室的人关灯(此时办公室已没有人需要照明)

为判断是否还有人在办公室里,这里导入计数功能来计算“需要照明的人数”。下面让我们这一功能是如何运作的吧。
(1)第一个人进入办公室,需要照明的人数+1.计数从0变成了1,因此要开灯
(2)之后当有人进入办公室的时候,需要照明的人数就+1。如计数从1变成2.
(3)每当有人下班离开办公室的时候,需要照明是人数就-1,如计数从2变成1.
(4)最后一个人下班离开办公室的时候,需要照明的人数-1.计数从1变成了0,因此要关灯。
这样就可以在不需要照明灯情况下 保持关灯的状态,办公室仅有的照明设备得到了很好的利用。
在Objective-C中,“对象”相当于办公室的照明设备,在现实世界中办公室的照明设备只有一个,但在ObjectIve-C的世界里,虽然计算机资源有限,但一台计算机可以同时处理多个对象。
此外”对象的使用环境“相当于上班进入办公室吧的人。虽然这里的”环境“有时候也指在运行中的程序代码,变量,变量作用域,对象等,但在概念上就是使用对象的环境。上班进入办公室的人对照明设备发出的动作,与ObjectIve-C中的对应关系是

对照明设备所做的动作 对Objective-C对象所做的动作
开灯 生成对象
需要照明 持有对象
不需要照明 释放对象
关灯 废弃对象

使用基数功能计算需要照明的人数,是办公室的照明得到了很好的管理。同样使用计数功能,对象也能得到很好的管理,这就是Objective-C的内存管理。

1.3 内存管理的思考方式

引用计数内存管理的思考方式。看到引用计数这个名称 我们便会不自觉的联想到“某处某物多多少少” 而将注意力放在计数上面。但其实,更贱客观,正确的思考方式是:
<自己生成的对象,自己持有
<非自己生成的对象,自己也能持有
<不再需要自己持有的对象时释放
<非自己持有的对象不能释放
引用计数式内存管理的思考方式仅此而已。按照这个思路,完全不必要考虑引用计数。
上文出现了“生成”,“持有”,“释放”三个词。而在Objective-C内存管理中还要加上“废弃”一词,这四个词频繁出现,而各个词语表示的Objective-C方法如下
对象操作 Objective-C方法
生成并持有对象 alloc/new/copy/mutableCopy等方法
持有对象 retain
释放对象 release
废弃 dealloc
这些有关Objective-C内存管理的方法,实际上不包括在Objective-C语言中,而是包含在Cocoa框架中用于OS X,iOS应用开发。Cocoa框架中Foundation框架类库的NSObject类担负内存管理的职责。Objective-C内存管理中的alloc/retain/release/dealloc方法分别只带NSobject类的alloc方法,retain方法,release方法和dealloc实例方法。
自己生成的对象,自己所持有
使用一下名称开头的方法名意味着自己生成的对象只有自己持有,在这里自己是指对象的使用环境

alloc

    /**
     自己生成并持有对象
     */
    id obj =[[NSObject alloc]init];

使用NSObject类的alloc方法就能自己生成并持有对象。指向生成并持有对象的指针被赋予变量obj

new

    /**
     自己生成并持有对象
     */
    id obj =[NSObject new];

copy >mutbleCopy
copy方法是基于NSCopying方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本。与copy方法类似mutableCopy方法是基于mutableCopying约定,由各类实现的mutableCopyWithZone:方法生成并持有对象,虽然是对象的副本,但是同alloc、new、方法一样,在“自己生成并持有对象”,这点上没有改变
另外,根据上述“使用一下名称开头的方法名”,下列名称也意味着自己生成并持有吧对象。
allocMyObject
newThatObject
copyThis
mutableCopyYourObject
非自己生成的对象,自己也能持有
用上述项目之外的方法取得的对象,即用alloc/new/copy/mutableCopy以外的方法取得的对象,因为非自己生成并持有,所以自己不是该对象的持有者。我们来使用alloc/copy/new/mutableCopy以外的方法看看,这里试用NSMutableArray的array方法

    /**
     取得非自己生成并持有对象
     */
    id obj =[NSMutableArray array];

源代码中NSMutableArray类对象被赋予变量obj 但变量obj自己并不持有该对象,使用retain方法可以持有对象

    /**
     取得非自己生成并持有对象
     */
    id obj =[NSMutableArray array];
    /**
     *  取得对象存在,但自己不持有对象
     */
    [obj retain];
    /**
     *  自己持有对象
     */

通过retain方法,非自己生成的对象,跟用alloc/new/copy/mutableCopy方法生成并持有的对象一样,成为了自己所持有的。

不在需要自己持有的对象时释放
自己持有的对象,一旦不在需要,持有者有义务释放该对象。释放是用release方法

    /**
     自己生成并持有对象
     */
    id obj =[[NSObject alloc]init];
    [obj release];
    /**
     *  释放对象
     */

如此,用alloc方法由自己生成并持有的对象就通过release方法释放了,自己生成而非自己持有的对象,若用retain方法变为自己持有,也同样需要release方法释放

    /**
     取得非自己生成并持有对象
     */
    id obj =[NSMutableArray array];
    /**
     *  取得对象存在,但自己不持有对象
     */
    [obj retain];
    [obj release];

用alloc/new /copy/mutableCopy方法生成并持有的对象,或者用retain方法生成并持有的对象,一旦不在需要,务必需用release方法进行释放
另外我们会经常在开发中遇到autorelease一词,那么autorelease和release有什么区别的联系呢?
autorelease提供这样的功能,是对象在超出指定的生存范围时能够自动并正确的释放(调用release方法)
release是理解释放 autorelease并不是理解释放,而是注册到自动释放池autoreleasePool中,pool结束的时候再进行自动调用release释放
,例如通过NSMutableArray的array方法可以取得谁都不持有的对象,这些方法都是通过autorelease而实现的。
无法释放非自己持有的对象
对于用alloc/new/copy/mutableCopy方法生成并持有的对象,或使用retain方法持有的对象,由于持有者是自己,所以在不需要该对象时需要将其释放,而由此以外所得到的对象绝对不能释放。倘若应用程序中释放了非自己所持有的对象,就会造成程序崩溃
以上四项内容就是 ”引用计数式内存管理“ 的思考方式

1.4 苹果的实现####

因为NSObject类的源代码没有公开,此处利用Xcode的调试器(lldb)和iOS大概追溯其实现工程。在NSObject类的方法alloc上设置断点,醉追溯程序的执行。以下是程序执行所调用的方法和函数。
+ alloc
+ allocWithZone:
class_createInstance
calloc
alloc首先调用allocWithZone:类方法,然后调用class_createInstance函数,该函数在Objective-C运行时参考中也有说明,然后通过调用calloc来分配内存块。 class_createInstance函数的源代码可以通过objc4库中的runtime/objc-runtime-new.mm进行确认。
retainCount/release/retain的实现又是怎样的呢?和刚才的方法一样,在这里列出了各个方法调用的方法和函数:
- retainCount
__CFDoExternRefOperation
CFBasicHashGetCountOfKey

- retain
__CFDoExternRefOperation
CFBasicHashAddValue

- release
__CFDoExternRefOperation
CFBasicHashRemoveValue  (CFBasicHashRemoveValue 返回0时,-release调用dealloc方法)

各个方法都通过调用了一个 _ _CFDoExternRefOperation函数,调用了一系列名称相似的函数。如这些函数名的前缀“CF”,他们包含于 Core Foundation框架源代码中,即是CFRuntime.c _ _ _CFDoExternRefOperation函数。为了理解其实现,下面简化了__CFDoExternRefOperation后的源代码。

CF/CFRuntime.c __CFDoExternRefOperation

int __CFDoExternRefOperation(uintptr_t op, id obj){
    CFBasicHashRef table = 取得对象对应的散列表(obj);
    int count;
    switch (op) {
        case OPERATION_retaionCount:
            count = CFBasicHashGetCountOfKey(table,obj);
            return count;

        case OPERATION_retaion:
            count = CFBasicHashAddValue(table,obj);
            return obj;

        case OPERATION_release:
            count = CFBasicHashRemoveValue(table,obj);
            return 0 == count;
    }
}

__CFDoExternRefOperation函数按retainCount/retain/release操作进行分发,调用不同的函数。NSObject类的retainCount/retain/release实例方法也许如下面代码所示:

-(NSUInteger)retainCount{
    return (NSUInteger)__CFDoExternRefOperation(OPERATION_retaionCount,self);
}
-(id)retain{  
    return (id)__CFDoExternRefOperation(OPERATION_retaion,self);
}
-(void)release{
    return (NSUInteger)__CFDoExternRefOperation(OPERATION_release,self);
}

可以从__CFDoExternRefOperation函数以及由此函数调用的各个函数名看出,苹果的实现大概就是采用散列表(引用计数表)来管理引用计数。

1.5 autorelease####

autorelease就是自动释放,这看上去很像ARC,但实际上他更类似于C语言中的自动变量(局部变量的特征)。
在C语言中,若某自动变量超出其作用域,该自动变量将被自动遗弃。
autorelease会像C语言的自动变量那样来对待对象实例。当超出其作用域(相当于变量作用域)时,对象实例的release实例方法将会被调用。另外,同C语言不同的是,变成人员可以设置变量的作用域。

autorelease的具体使用方法如下,
(1)生成并持有NSAutoreleasePool对象
(2)调用已分配对象的autorelease实例方法
(3)废弃NSAutoreleasePool对象

NSAutoreleasePool对象的生存周期相当于C语言变量的作用域。对于所有调用autorelease实例方法的对象,在废弃NSAutoreleasePool对象时,都将调用release实例方法。代码表示如下:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    id obj = [[NSObject alloc]init];
    [obj autorelease];
    [pool drain];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容